package org.light.core;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.light.complexverb.AddUploadDomainField;
import org.light.complexverb.ListMyActive;
import org.light.domain.Controller;
import org.light.domain.Domain;
import org.light.domain.Dropdown;
import org.light.domain.Field;
import org.light.domain.Method;
import org.light.domain.Statement;
import org.light.domain.StatementList;
import org.light.domain.ValidateInfo;
import org.light.exception.ValidateException;
import org.light.utils.DomainUtil;
import org.light.utils.StringUtil;
import org.light.utils.WriteableUtil;
import org.light.verb.FindById;
import org.light.verb.ListActive;

public class AxumPlainLoginController extends AxumController implements Comparable<Controller>{
	private static final long serialVersionUID = 6506582842158817100L;
	protected Domain userDomain;
	protected Domain roleDomain;
	protected Domain privilegeDomain;

	public Domain getUserDomain() {
		return userDomain;
	}

	public void setUserDomain(Domain userDomain) {
		this.userDomain = userDomain;
	}

	public Domain getRoleDomain() {
		return roleDomain;
	}

	public void setRoleDomain(Domain roleDomain) {
		this.roleDomain = roleDomain;
	}

	public Domain getPrivilegeDomain() {
		return privilegeDomain;
	}

	public void setPrivilegeDomain(Domain privilegeDomain) {
		this.privilegeDomain = privilegeDomain;
	}

	public AxumPlainLoginController(Domain domain) throws ValidateException {
		super(domain);
		this.domain = domain;
	}
	
	public StatementList getControllerRoutesStatementList() throws ValidateException {
		List<Writeable> sList = new ArrayList<Writeable>();
		sList.add(new Statement(1000L,0,"pub fn login_"+StringUtil.getSnakeName(this.userDomain.getControllerSuffix())+"() -> Router {"));
		sList.add(new Statement(2000L,1,"Router::new()"));
		sList.add(new Statement(3000L,1,".route(\"/login"+this.userDomain.getCapFirstDomainName()+"\", post(login_"+this.userDomain.getSnakeDomainName()+"))"));
		sList.add(new Statement(4000L,1,".route(\"/logout"+this.userDomain.getCapFirstDomainName()+"\", get(logout_"+this.userDomain.getSnakeDomainName()+"))"));
		sList.add(new Statement(5000L,1,".route(\"/readMySession\", get(read_my_session))"));
		sList.add(new Statement(6000L,1,".route(\"/register"+this.userDomain.getCapFirstDomainName()+"\", post(register_"+this.userDomain.getSnakeDomainName()+"))"));
		 
		long serial = 7000L;
		if (this.userDomain.containsImage()) {
			for (Field f:this.userDomain.getPlainFields()) {
				if ("Image".equalsIgnoreCase(f.getFieldType())) {
					AddUploadDomainField au = new AddUploadDomainField(this.userDomain,f);
					sList.add(new Statement(serial,1,".route(\"/"+StringUtil.lowerFirst(au.getVerbName())+"\", post("+StringUtil.getSnakeName(au.getVerbName())+"))"));
					serial += 1000L;
				}
			}
		}
		
		List<Domain> translateDomains = new ArrayList<Domain>();
		for (Field f: this.domain.getPlainFields()){
			if (f instanceof Dropdown){
				Dropdown dp = (Dropdown)f;
				Domain target = dp.getTarget();
				if (!target.isLegacy()&&!DomainUtil.inDomainList(target, translateDomains)){
					translateDomains.add(target);
				}
			}
		}
		
		for (Domain d:translateDomains) {
			ListActive list = new ListActive(d);
			sList.add(new Statement(serial,1,".route(\"/"+StringUtil.lowerFirst(list.getVerbName())+"\", post("+StringUtil.getSnakeName(list.getVerbName())+"))"));
			serial += 1000L;
		}
		
		sList.add(new Statement(serial,1,".layer(TraceLayer::new_for_http())"));
		sList.add(new Statement(serial + 1000L,0,"}"));
		return WriteableUtil.merge(sList);
	}

	@Override
	public String generateControllerString() throws ValidateException {
		StringBuilder sb = new StringBuilder();
		Set<String> imports = this.generateImportStrings();
		imports.add("serde_json::{Value,Map}");
		imports.add("std::collections::HashMap");
		imports.add("tower_http::{trace::TraceLayer}");
		imports.add("crate::"+StringUtil.getSnakeName(this.userDomain.getControllerSuffix())+"::"+this.userDomain.getSnakeDomainName()+"_"+StringUtil.getSnakeName(this.userDomain.getControllerSuffix())+"::"+this.userDomain.getCapFirstDomainName()+"Request");
		if (this.userDomain.containsImage()) {
			for (Field f:this.userDomain.getPlainFields()) {
				if ("Image".equalsIgnoreCase(f.getFieldType())) {
					imports.add("crate::"+StringUtil.getSnakeName(this.userDomain.getControllerSuffix())+"::"+this.userDomain.getSnakeDomainName()+"_"+StringUtil.getSnakeName(this.userDomain.getControllerSuffix())+"::pick_"+this.userDomain.getSnakeDomainName()+"_picture");
					imports.add("crate::"+StringUtil.getSnakeName(this.userDomain.getControllerSuffix())+"::"+this.userDomain.getSnakeDomainName()+"_"+StringUtil.getSnakeName(this.userDomain.getControllerSuffix())+"::store_"+this.userDomain.getSnakeDomainName()+"_picture");
				}
			}
		}
		
		if (this.domain.containsDateTime()) {
			imports.add("chrono::{NaiveDateTime,NaiveDate}");
			imports.add("crate::"+this.domain.getDomainSuffix()+"::my_date_time_format::DATE_TIME_FORMAT");
			imports.add("crate::"+this.domain.getDomainSuffix()+"::my_date_format::DATE_FORMAT");
		}
		
		List<Domain> translateDomains = new ArrayList<Domain>();
		for (Field f: this.domain.getPlainFields()){
			if (f instanceof Dropdown){
				Dropdown dp = (Dropdown)f;
				Domain target = dp.getTarget();
				if (!target.isLegacy()&&!DomainUtil.inDomainList(target, translateDomains)){
					translateDomains.add(target);
				}
			}
		}
		
		for (Domain d:translateDomains) {
			ListActive list = new ListActive(d);
			imports.add("crate::"+d.getServiceimplSuffix()+"::"+d.getSnakeDomainName()+"_service::"+StringUtil.getSnakeName(list.getVerbName())+" as service_"+StringUtil.getSnakeName(list.getVerbName()));
		}
		
		imports.add("crate::utils::jwt_util::Claims");
		imports.add("jsonwebtoken::DecodingKey");
		imports.add("crate::utils::jwt_util::JWT_SECRET");
		imports.add("jsonwebtoken::Validation");
		imports.add("std::collections::HashSet");
		
		imports.add("axum::{\r\n"
				+ "    extract::{Form, Path, Query, Json, Multipart,DefaultBodyLimit},\r\n"
				+ "    http:: {HeaderMap, HeaderValue,StatusCode},\r\n"
				+ "    routing::{get_service,get,post,MethodRouter},\r\n"
				+ "    response::{IntoResponse, Response,Redirect},\r\n"
				+ "    body::{Full, Bytes},\r\n"
				+ "    Router\r\n"
				+ "}");
		imports.add("axum_sessions::{\r\n"
				+ "    extractors::{WritableSession,ReadableSession},\r\n"
				+ "}");
		
		imports.add("crate::"+this.domain.getDomainSuffix()+"::"+this.domain.getCapFirstDomainNameWithSuffix());
		imports.add("crate::"+this.domain.getDomainSuffix()+"::"+this.domain.getCapFirstDomainName()+"QueryRequest");
		imports.add("crate::"+this.domain.getDomainSuffix()+"::dto::LoginInput");
		imports.add("crate::"+this.domain.getDomainSuffix()+"::dto::TokenPayload");
		imports.add("crate::"+this.userDomain.getDomainSuffix()+"::error::Error");
		imports.add("crate::utils::jwt_util");
		ListMyActive lmaur = new ListMyActive(this.userDomain,this.roleDomain);
		ListMyActive lmarp = new ListMyActive(this.roleDomain,this.privilegeDomain);
		imports.add("crate::"+this.userDomain.getServiceimplSuffix()+"::"+this.userDomain.getSnakeDomainName()+"_service::"+StringUtil.getSnakeName(lmaur.getVerbName())+" as service_"+StringUtil.getSnakeName(lmaur.getVerbName()));
		imports.add("crate::"+this.roleDomain.getServiceimplSuffix()+"::"+this.roleDomain.getSnakeDomainName()+"_service::"+StringUtil.getSnakeName(lmarp.getVerbName())+" as service_"+StringUtil.getSnakeName(lmarp.getVerbName()));
		imports.add("crate::"+this.userDomain.getServiceimplSuffix()+"::login_service::login_"+this.userDomain.getSnakeDomainName()+" as service_login_"+this.userDomain.getSnakeDomainName());
		imports.add("crate::"+this.userDomain.getServiceimplSuffix()+"::"+this.userDomain.getSnakeDomainName()+"_service::register_"+this.userDomain.getSnakeDomainName()+" as service_register_"+this.userDomain.getSnakeDomainName());
		FindById findUser = new FindById(this.userDomain);
		imports.add("crate::"+this.userDomain.getServiceimplSuffix()+"::"+this.userDomain.getSnakeDomainName()+"_service::"+StringUtil.getSnakeName(findUser.getVerbName())+" as service_"+StringUtil.getSnakeName(findUser.getVerbName()));
		imports.addAll(this.classImports);
		String importsStr = DomainUtil.generateImportStr(imports);
		sb.append("#![allow(unused_imports)]\n");
		sb.append(importsStr);
		sb.append("\n");
		sb.append(this.getControllerRoutesStatementList().getContent());
		sb.append("\n");

		Iterator it2 = this.getMethods().iterator();
		while (it2.hasNext()) {
			sb.append(((Method) it2.next()).generateMethodString()).append("\n");
		}		
		return sb.toString();
	}

	public ValidateInfo validate() {
		ValidateInfo info = new ValidateInfo();
		info.setSuccess(true);
		if (this.verbs == null || this.verbs.size() == 0) {
			info.setSuccess(false);
			info.addCompileError("Controller " + this.standardName
					+ " 动词是空！");
		}
		for (Verb v : this.verbs) {
			if (v != null && v.getDomain() == null) {
				info.setSuccess(false);
				info.addCompileError("Controller " + this.standardName + "动词"
						+ v.getVerbName() + "域对象是空值！");
			}
		}
		return info;
	}

	public AxumPlainLoginController(List<Verb> verbs, Domain domain,Boolean ignoreWarning)
			throws ValidateException {
		super(domain);
		this.domain = domain;
		try{
			for (Verb v : verbs) {
				v.setDomain(domain);
			}
			this.verbs = verbs;
			this.standardName = "Login" + domain.getControllerNamingSuffix();
	
			ValidateInfo info = this.validate();
			if (info.success(ignoreWarning)) {
				for (Field f:domain.getFields()) {
					if (f instanceof Dropdown) {
						Dropdown dp = (Dropdown)f;
					}
				}
			} else {
				ValidateException e = new ValidateException(info);
				throw e;
			}
		}catch (Exception ex){
			ValidateInfo info = new ValidateInfo();
			info.addCompileError("AxumPlainLogin控制器新建失败！");
			ValidateException e = new ValidateException(info);
			throw e;
		}
	}
}
